// Copyright 2011 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * Register an action representing a C++ library.
 *
 * The following keys are supported in the object:
 *
 *   name (string)
 *     The name of the library. If the source file is called foo.cc, this is
 *     often 'foo'.
 *
 *   hdrs (Array.<string>)
 *     A set of header files that dependents of this library should be allowed
 *     to include, specified as paths relative to the current package. It is
 *     important to specify these correctly; leaving one out may cause a rebuild
 *     to not be triggered correctly when header files change.
 *
 *   srcs (Array.<string>)
 *     A set of source files comprising this library, specified as paths
 *     relative to the current package. These will be compiled to object files
 *     that will be included in any binary built containing this library.
 *
 *   deps (Array.<string>)
 *     A set of targets upon which this library depends. These must be other
 *     cc_library targets. Their object files will be included in any binary
 *     that includes this library.
 *
 * @param {!Object} args
 *     An object with keys as documented above.
 */
flame.cc_library = function(args) {
  var srcs = args['srcs'] || [];
  var deps = args['deps'] || [];
  var hdrs = args['hdrs'] || [];

  // TODO(jacobsa): Type checking.

  // Sanity-check: each source file must end in .cc.
  if (!srcs.every(function(src) { return src.substr(-3) == '.cc')) {
    throw new Error(
        'Invalid srcs arg to ' + name + '; every source must end in .cc');
  }

  // Make absolute path lists of the srcs and hdrs attributes of this target.
  var packageDir = flame.currentPackageDir();
  function prependPackageDir(x) { return packageDir + '/' + x; }

  var absoluteHdrs = hdrs.map(prependPackageDir);
  var absoluteSrcs = srcs.map(prependPackageDir);

  // Also make a list of object files that will be produced.
  function makeObjPath(src) { return src.substr(0, src.length - 3) + '.o'; }
  var absoluteObjs = absoluteSrcs.map(makeObjPath);

  // Invoke g++ once per source file. Keep track of the object files we'll
  // produce.
  function makeCommand(src, i) {
    return 'g++ -o ' + absoluteObjs[i] + ' ' + src;
  }

  var commands = absoluteSrcs.map(makeCommand);

  // We require that the dependency actions be resolved before we continue.
  flame.resolveActions(deps, function() {
    // Build a list of all of the headers and object files pulled in
    // transitively by this target's dependencies.
    var transitiveHdrs =
        deps.map(function(dep) {
          return flame.getAction(dep).userInfo['transitiveHdrs'];
        });

    var transitiveObjs =
        deps.map(function(dep) {
          return flame.getAction(dep).userInfo['transitiveObjs'];
        });

    function concatReduce(a, b) { return a.concat(b); }
    transitiveHdrs = transitiveHdrs.reduce(concatReduce);
    transitiveObjs = transitiveObjs.reduce(concatReduce);

    // Include the headers and objects for this target.
    transitiveHdrs = transitiveHdrs.concat(absoluteHdrs);
    transitiveObjs = transitiveObjs.concat(absoluteObjs);

    // Register this action. Use both the source files and the transitive
    // headers as inputs, so that the action gets re-run if any of our
    // dependencies' headers change.
    flame.registerAction(
        packageDir + ':' + name,
        transitiveHdrs.concat(absoluteSrcs),
        absoluteObjs,
        commands,
        {
          'transitiveHdrs': transitiveHdrs,
          'transitiveObjs': transitiveObjs,
        },
    );
  });
};
